/* window.c, Ait, Kevin Bloom, BSD 3-Clause, 2023 */

#include "header.h"
#include "termbox.h"

int win_cnt = 0;

window_t* new_window()
{
  window_t *wp = (window_t *)malloc(sizeof(window_t));

  assert(wp != NULL); /* call fatal instead XXX */
  wp->w_next = NULL;
  wp->w_bufp = NULL;
  wp->w_point = 0;
  wp->w_mark = NOMARK;
  wp->w_top = 0;
  wp->w_left = 0;
  wp->w_rows = 0;
  wp->w_cols = 0;
  wp->w_update = FALSE;
  sprintf(wp->w_name, "W%d", ++win_cnt);
  return wp;
}

void one_window(window_t *wp)
{
  window_mode = WINDOW_DEFAULT;
  wp->w_top = 0;
  wp->w_left = 0;
  wp->w_rows = LINES - 2;
  wp->w_cols = COLS;
  wp->w_next = NULL;
  wp->w_update = TRUE;
}

void delete_other_windows_local(int showmsg)
{
  if (wheadp->w_next == NULL) {
    if(showmsg)
      msg("Only 1 window");
    return;
  }
  free_other_windows(curwp);
}

void split_window_local(int internal)
{
  window_t *wp, *wp2;
  int ntru, ntrl;

  if(!internal && window_mode == WINDOW_HORIZ) {
    msg("Already in horizontal mode!");
    return;
  }
  if(!internal) {
    window_mode = WINDOW_HORIZ;
    delete_other_windows_local(FALSE);
  }

  if (curwp->w_rows < 3) {
    msg("Cannot split a %d line window", curwp->w_rows);
    return;
  }

  wp = new_window();
  if(curwp->w_bufp->b_next != NULL)
    associate_b2w(curwp->w_bufp->b_next,wp);
  else
    associate_b2w(curwp->w_bufp,wp);
  b2w(wp); /* inherit buffer settings */

  ntru = (curwp->w_rows - 1) / 2; /* Upper size */
  ntrl = (curwp->w_rows - 1) - ntru; /* Lower size */

  /* Old is upper window */
  curwp->w_rows = ntru;
  wp->w_top = curwp->w_top + ntru + 1;
  wp->w_rows = ntrl;
  wp->w_cols = curwp->w_cols;
  wp->w_left = curwp->w_left;

  /* insert it in the list */
  wp2 = curwp->w_next;
  curwp->w_next = wp;
  wp->w_next = wp2;
  redraw(); /* mark the lot for update */
}

void split_window()
{
  split_window_local(FALSE);
}

void tri_split_window()
{
  window_t *wp, *wp2, *wp3;
  int ntru;

  if(window_mode == WINDOW_TRIHORIZ) {
    msg("Already in triple horizontal mode!");
    return;
  }
  window_mode = WINDOW_TRIHORIZ;
  delete_other_windows_local(FALSE);

  if (curwp->w_rows < 3) {
    msg("Cannot split a %d line window", curwp->w_rows);
    return;
  }

  wp = new_window();
  if(curwp->w_bufp->b_next != NULL)
    associate_b2w(curwp->w_bufp->b_next,wp);
  else
    associate_b2w(curwp->w_bufp,wp);
  b2w(wp); /* inherit buffer settings */

  wp2 = new_window();
  if(wp->w_bufp->b_next != NULL)
    associate_b2w(wp->w_bufp->b_next,wp2);
  else
    associate_b2w(curwp->w_bufp,wp2);
  b2w(wp2); /* inherit buffer settings */

  ntru = (curwp->w_rows - 1) / 3; /* Upper size */
//  ntrl = (curwp->w_rows - 3) - ntru; /* Lower size */
  /* Old is upper window */
  curwp->w_rows = ntru;
  wp->w_top = curwp->w_top + ntru + 1;
  wp->w_rows = ntru;
  wp->w_cols = curwp->w_cols;
  wp->w_left = curwp->w_left;
  wp2->w_top = wp->w_top + ntru + 1;
  wp2->w_rows = ntru + ((curwp->w_rows-2) % 3 != 0 ? 1 : 0);
  wp2->w_cols = curwp->w_cols;
  wp2->w_left = curwp->w_left;

  /* insert it in the list */
  wp3 = curwp->w_next;
  curwp->w_next = wp;
  wp->w_next = wp2;
  wp2->w_next = wp3;
  redraw(); /* mark the lot for update */
}

void chop_window_local(int internal)
{
  window_t *wp, *wp2;
  int ntru, ntrl;

  if(!internal && window_mode == WINDOW_VERT) {
    msg("Already in vertical mode!");
    return;
  }
  if(!internal) {
    window_mode = WINDOW_VERT;
    delete_other_windows_local(FALSE);
  }
  if (curwp->w_cols < 22) {
    msg("Cannot split a %d columned window", curwp->w_cols);
    return;
  }

  wp = new_window();
  if(curwp->w_bufp->b_next != NULL)
    associate_b2w(curwp->w_bufp->b_next,wp);
  else
    associate_b2w(curwp->w_bufp,wp);
  b2w(wp); /* inherit buffer settings */

  ntru = (curwp->w_cols - 1) / 2; /* Upper size */
  ntrl = (curwp->w_cols - 1) - ntru; /* Lower size */
  /* Old is upper window */
  curwp->w_cols = ntru;
  wp->w_rows = curwp->w_rows;
  wp->w_top = curwp->w_top;
  wp->w_left = curwp->w_left + ntru + 1;
  wp->w_cols = ntrl;

  /* insert it in the list */
  wp2 = curwp->w_next;
  curwp->w_next = wp;
  wp->w_next = wp2;
  redraw(); /* mark the lot for update */
}

void chop_window()
{
  chop_window_local(FALSE);
}

void tri_chop_window()
{
  window_t *wp, *wp2, *wp3;
  int ntru;

  if(window_mode == WINDOW_TRIVERT) {
    msg("Already in triple vertical mode!");
    return;
  }
  window_mode = WINDOW_TRIVERT;
  delete_other_windows_local(FALSE);

  if (curwp->w_cols < 22) {
    msg("Cannot split a %d columned window", curwp->w_cols);
    return;
  }

  wp = new_window();
  if(curwp->w_bufp->b_next != NULL)
    associate_b2w(curwp->w_bufp->b_next,wp);
  else
    associate_b2w(curwp->w_bufp,wp);
  b2w(wp); /* inherit buffer settings */

  wp2 = new_window();
  if(wp->w_bufp->b_next != NULL)
    associate_b2w(wp->w_bufp->b_next,wp2);
  else
    associate_b2w(curwp->w_bufp,wp2);
  b2w(wp2); /* inherit buffer settings */

  ntru = (curwp->w_cols - 2) / 3; /* Upper size */
  /* Old is upper window */
  curwp->w_cols = ntru;
  wp->w_rows = curwp->w_rows;
  wp->w_top = curwp->w_top;
  wp->w_left = curwp->w_left + ntru + 1;
  wp->w_cols = ntru;
  wp2->w_rows = curwp->w_rows;
  wp2->w_top = curwp->w_top;
  wp2->w_left = curwp->w_left + wp->w_left + ntru + 1;
  wp2->w_cols = ntru;

  /* insert it in the list */
  wp3 = curwp->w_next;
  curwp->w_next = wp;
  wp->w_next = wp2;
  wp2->w_next = wp3;
  redraw(); /* mark the lot for update */
}

void next_window()
{
  curwp->w_update = TRUE; /* make sure modeline gets updated */
  curwp = (curwp->w_next == NULL ? wheadp : curwp->w_next);
  curbp = curwp->w_bufp;

  if (curbp->b_cnt > 1)
    w2b(curwp); /* push win vars to buffer */
}

void fib_left()
{
  if(window_mode == WINDOW_FIBLEFT) {
    msg("Already in Fibonacci left mode!");
    return;
  }
  window_mode = WINDOW_FIBLEFT;
  delete_other_windows_local(FALSE);
  chop_window_local(TRUE);
  split_window_local(TRUE);
  next_window();
  next_window();
  next_buffer();
  next_window();
}

void fib_right()
{
  if(window_mode == WINDOW_FIBRIGHT) {
    msg("Already in Fibonacci right mode!");
    return;
  }
  window_mode = WINDOW_FIBRIGHT;
  delete_other_windows_local(FALSE);
  chop_window_local(TRUE);
  next_window();
  split_window_local(TRUE);
  next_window();
  next_window();
}

void quad_window()
{
  if(window_mode == WINDOW_QUAD) {
    msg("Already in quad-window mode!");
    return;
  }
  window_mode = WINDOW_QUAD;
  delete_other_windows_local(FALSE);
  chop_window_local(TRUE);
  split_window_local(TRUE);
  next_window();
  next_window();
  next_buffer();
  split_window_local(TRUE);
  next_window();
  next_window();
}

void delete_other_windows()
{
  delete_other_windows_local(TRUE);
}

void free_other_windows(window_t *winp)
{
  window_t *wp, *next;

  for (wp = next = wheadp; next != NULL; wp = next) {
    next = wp->w_next; /* get next before a call to free() makes wp undefined */
    if (wp != winp) {
      disassociate_b(wp); /* this window no longer references its buffer */
      free(wp);
    }
  }

  wheadp = curwp = winp;
  one_window(winp);
}

void associate_b2w(buffer_t *bp, window_t *wp) {
  assert(bp != NULL);
  assert(wp != NULL);
  wp->w_bufp = bp;
   bp->b_cnt++;
}

void disassociate_b(window_t *wp) {
  assert(wp != NULL);
  assert(wp->w_bufp != NULL);
  wp->w_bufp->b_cnt--;
}

/* Recenters the screen whilst keeping the point.
   Will cycled from center, top, bottom.
   Because of odd number of rows, "middle" is considered anything that is in
   the range of [-1, 1].
 */
void recenter()
{
  int i = curwp->w_rows / 2;
  point_t new_page = curbp->b_page;
  int row = curwp->w_row - curwp->w_top;
  int shift = row - i;
  int current, lastln;
  assert(curwp != NULL);
  assert(curbp != NULL);

  get_line_stats(&current, &lastln, curbp);

  if(current == 0) {
    msg("Beginning of buffer, can't recenter");
    return;
  }
  if(shift == 0 || shift == 1 || shift == -1) // middle of screen
  {
    shift = curwp->w_rows / 2;
  } else if(row == curwp->w_rows - 1) // end of screen
  {
    shift = curwp->w_rows / 2;
  } else if(row == 0) // start of screen
  {
    shift = -1 * (curwp->w_rows - 1);
  }

  if(shift < 0) {
    for(int k = shift ; k < 0; k++) {
      new_page = upup(curbp, curwp, new_page);
    }
    if(*ptr(curbp, new_page) == '\n')
      new_page++;
  } else {
    for(int k = shift; k > 0; k--) {
      new_page = dndn(curbp, curwp, new_page);
    }
  }
  curbp->b_page = new_page;
}
